package datastructure.blockingqueue;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 单锁实现阻塞队列
 *
 * @author 杨 强
 * @package datastructure.blockingqueue
 * @createTime 2023-03-18 22:17
 */
public class OneLockBlockingQueue<E> implements BlockingQueue<E>{
    /**
     * 储存数据的数组
     */
    private final E[] array;
    /**
     * 尾指针,就是该指针的大小就是添加元素应该存储的下标.
     */
    private int head = 0;
    /**
     * 头指针,就是该指针的大小就是取元素的下标.
     */
    private int tail = 0;
    /**
     * 队列元素个数
     */
    private int size = 0;

    /**
     * 构造器初始化队列容量
     * @param capacity
     */
    public OneLockBlockingQueue(int capacity) {
        array = (E[]) new Object[capacity];
    }

    /**
     * JDK的ReentrantLock锁
     */
    ReentrantLock lock = new ReentrantLock();
    /**
     * 条件队列1
     */
    Condition tailWaits = lock.newCondition();
    /**
     * 条件队列2
     */
    Condition headWaits = lock.newCondition();

    @Override
    public void offer(E e) throws InterruptedException {
        // 加可以被打断的锁
        lock.lockInterruptibly();
        try {
            // 判断是否满了,用while是为了唤醒也继续判断一下,防止虚假唤醒
            while (isFull()) {
                // 满了就在条件队列阻塞.
                tailWaits.await();
            }
            // 添加数据
            array[tail] = e;
            // tail指针+1, 并且判断加一后是否等于数组长度,等于的话就又从0开始.
            if (++tail == array.length) {
                tail = 0;
            }
            // 队列元素加一
            size++;
            // 唤醒阻塞在tailWaits条件队列的线程,进行添加元素了
            headWaits.signal();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    @Override
    public void offer(E e, Long timeout) throws InterruptedException {
        // 加可以被打断的锁
        lock.lockInterruptibly();
        try {
            // 毫秒转纳秒
            long t = TimeUnit.MILLISECONDS.toNanos(timeout);
            // 判断是否满了,用while是为了唤醒也继续判断一下,防止虚假唤醒
            while (isFull()) {
                // 时间阻塞到位了就直接退出了.
                if (t <= 0) {
                    return;
                }
                // 满了就阻塞对应的时间, 返回值是还剩多少时间没有等
                t = tailWaits.awaitNanos(t);
            }
            array[tail] = e;
            if (++tail == array.length) {
                tail = 0;
            }
            size++;
            headWaits.signal();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public E poll() throws InterruptedException {
        // 加可以被打断的锁
        lock.lockInterruptibly();
        try {
            // 判断是否空,用while是为了唤醒也继续判断一下,防止虚假唤醒
            while (isEmpty()) {
                // 在headWaits条件队列阻塞
                headWaits.await();
            }
            // 取出元素
            E e = array[head];
            array[head] = null; // help GC
            if (++head == array.length) {
                head = 0;
            }
            size--;
            tailWaits.signal();
            return e;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 判空
     * @return
     */
    private boolean isEmpty() {
        return size == 0;
    }

    /**
     * 判满
     * @return
     */
    private boolean isFull() {
        return size == array.length;
    }
}
